#include <windows.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dcompiler.h>
#include <D3DVoxelChunk.h>
#include "D3DStreamOutDbg.h"

D3DVoxelChunk::D3DVoxelChunk(ID3D11Device *pDevice, ID3D11DeviceContext *pImmediateContext)
{
	
	m_pChunkData = NULL;
	m_pIndexBuffer = NULL;
	m_pVoxelData = NULL;
	m_pVoxelTextureSRV = NULL;
	m_pVoxelSamplerLinear = NULL;
	m_pVoxelMeshBuffer = NULL;
	m_pWorldMatrixBuffer = NULL;

	m_bMarkedDirty = true;

	m_pDevice = pDevice;
	m_pImmediateContext = pImmediateContext;
}
void Voxel::operator=( Voxel &src)
{
	cVolume = src.cVolume;
	cType = src.cType;
	cSubType = src.cSubType;
	cOwner = src.cOwner;
}
Voxel * D3DVoxelChunk::GetVoxel(int x, int y, int z)
{
	int idx = D3DVoxelConstants::CalcVoxelIndex(x,y,z);
	if(idx < 0 || idx >= D3DVoxelConstants::VoxelsPerChunkAxisCube)
	{
		return NULL;
	}
	return &m_pChunkData[idx];
}
void D3DVoxelChunk::UpdateVoxelTexture(void)
{
	//m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2,2,1)].cVolume = 200;
	m_pImmediateContext->UpdateSubresource(m_pVoxelData, 0, NULL, m_pChunkData, D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::BytesPerVoxel
		, D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::BytesPerVoxel);
	
}
HRESULT D3DVoxelChunk::InitIndexBuffer(UINT size)
{
	if(m_pIndexBuffer != NULL )
	{
		m_pIndexBuffer->Release();
		m_pIndexBuffer = NULL;
	}
	D3D11_BUFFER_DESC bufferDesc =
	{
		sizeof( UINT ) * size,
		D3D11_USAGE_DEFAULT,
		D3D11_BIND_STREAM_OUTPUT | D3D11_BIND_INDEX_BUFFER,
		0,
		0,
		0
	};
    return m_pDevice->CreateBuffer( &bufferDesc, NULL, &m_pIndexBuffer );
}

HRESULT D3DVoxelChunk::InitStage3Buffers(void)
{
	HRESULT hr = S_OK;

	/////////////////////////////////
	//		m_pVoxelMarkers
	/////////////////////////////////

	int iBufferSize = D3DVoxelConstants::VoxelsPerChunkAxisCube * sizeof(UINT);

	D3D11_BUFFER_DESC bufferDesc =
	{
		iBufferSize,
		D3D11_USAGE_DEFAULT,
		D3D11_BIND_STREAM_OUTPUT | D3D11_BIND_VERTEX_BUFFER,
		0,
		0,
		0
	};
	hr = m_pDevice->CreateBuffer( &bufferDesc, NULL, &m_pVoxelMarkers );
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateBuffer:m_pVoxelMarkers", L"Error", MB_OK );
        return hr;
    
	}

	/////////////////////////////////
	//		m_pEdgeMarkers
	/////////////////////////////////

	D3D11_BUFFER_DESC EdgeBufferDesc =
	{
		iBufferSize,
		D3D11_USAGE_DEFAULT,
		D3D11_BIND_STREAM_OUTPUT | D3D11_BIND_VERTEX_BUFFER,
		0,
		0,
		0
	};
	hr = m_pDevice->CreateBuffer( &EdgeBufferDesc, NULL, &m_pEdgeMarkers );
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateBuffer:m_pEdgeMarkers", L"Error", MB_OK );
        return hr;
    
	}

	/////////////////////////////////
	//		m_pVertexIDVolume
	/////////////////////////////////

	D3D11_TEXTURE3D_DESC VertexIDVolumeTextureDesc;

	ZeroMemory( &VertexIDVolumeTextureDesc, sizeof(VertexIDVolumeTextureDesc) );

	VertexIDVolumeTextureDesc.MipLevels = 1;
	VertexIDVolumeTextureDesc.Width = D3DVoxelConstants::VoxelsPerChunkAxis * 3;
	VertexIDVolumeTextureDesc.Height = D3DVoxelConstants::VoxelsPerChunkAxis;
	VertexIDVolumeTextureDesc.Depth = D3DVoxelConstants::VoxelsPerChunkAxis;
	VertexIDVolumeTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_RENDER_TARGET;
	VertexIDVolumeTextureDesc.CPUAccessFlags = 0;
	VertexIDVolumeTextureDesc.Usage = D3D11_USAGE_DEFAULT;
	VertexIDVolumeTextureDesc.Format = DXGI_FORMAT_R32_UINT;

	D3D11_SUBRESOURCE_DATA VertexIDVolumeZeroes;

	UINT* volumeZeroes = new UINT[D3DVoxelConstants::VoxelsPerChunkAxisCube * 3];

	for(int i = 0; i < D3DVoxelConstants::VoxelsPerChunkAxisCube * 3; i++)
	{
		volumeZeroes[i] = 0;
	}
	
	VertexIDVolumeZeroes.pSysMem = (void *) volumeZeroes;
	VertexIDVolumeZeroes.SysMemPitch = D3DVoxelConstants::VoxelsPerChunkAxis * 3;
	VertexIDVolumeZeroes.SysMemSlicePitch = D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::VoxelsPerChunkAxis * 3;

	hr = m_pDevice->CreateTexture3D(&VertexIDVolumeTextureDesc, &VertexIDVolumeZeroes, &m_pVertexIDVolume);
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateTexture3D:m_pVertexIDVolume", L"Error", MB_OK );
        return hr;
    
	}
	D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
	
	// Setup the description of the render target view.
	renderTargetViewDesc.Format = VertexIDVolumeTextureDesc.Format;
	renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
	renderTargetViewDesc.Texture3D.MipSlice = 0;
	renderTargetViewDesc.Texture3D.FirstWSlice = 0;
	renderTargetViewDesc.Texture3D.WSize = -1;

	// Create the render target view.
	hr = m_pDevice->CreateRenderTargetView(m_pVertexIDVolume, &renderTargetViewDesc, &m_pVertexIDVolumeRTV);
	if(FAILED(hr))
	{
		MessageBox( NULL,
                    L"CreateRenderTargetView:m_pVertexIDVolumeRTV", L"Error", MB_OK );
        return hr;
	}

	// Setup the description of the shader resource view.
	shaderResourceViewDesc.Format = VertexIDVolumeTextureDesc.Format;
	shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
	shaderResourceViewDesc.Texture3D.MostDetailedMip = 0;
	shaderResourceViewDesc.Texture3D.MipLevels = 1;

	// Create the shader resource view.
	hr = m_pDevice->CreateShaderResourceView(m_pVertexIDVolume, &shaderResourceViewDesc, &m_pVertexIDVolumeSRV);
	if(FAILED(hr))
	{
		MessageBox( NULL,
                    L"CreateShaderResourceView:m_pVertexIDVolumeSRV", L"Error", MB_OK );
        return hr;
	}


	return hr;

}
HRESULT D3DVoxelChunk::InitBuffers(void)
{
	HRESULT hr = S_OK;

	D3D11_BUFFER_DESC bd;
    // Create the constant buffers
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
    bd.ByteWidth = sizeof(CBWorldMatrix);
    hr = m_pDevice->CreateBuffer( &bd, NULL, &m_pWorldMatrixBuffer );
    if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateBuffer:m_pWorldMatrixBuffer", L"Error", MB_OK );
        return hr;
    
	}

	CBWorldMatrix cb;
	
	//D3DXMatrixIdentity(&(cb.matWorld));
	//m_transform.x = 2.0f;

	m_transform.CalculateMatrix(&(cb.matWorld));	
    m_pImmediateContext->UpdateSubresource( m_pWorldMatrixBuffer, 0, NULL, &cb, 0, 0 );
	

	D3D11_TEXTURE3D_DESC VertexIDVolumeTextureDesc;

	ZeroMemory( &VertexIDVolumeTextureDesc, sizeof(VertexIDVolumeTextureDesc) );

	VertexIDVolumeTextureDesc.MipLevels = 1;
	VertexIDVolumeTextureDesc.Width = D3DVoxelConstants::VoxelsPerChunkAxis;
	VertexIDVolumeTextureDesc.Height = D3DVoxelConstants::VoxelsPerChunkAxis;
	VertexIDVolumeTextureDesc.Depth = D3DVoxelConstants::VoxelsPerChunkAxis;

	VertexIDVolumeTextureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

	VertexIDVolumeTextureDesc.CPUAccessFlags = 0;
	VertexIDVolumeTextureDesc.Usage = D3D11_USAGE_DEFAULT;

	VertexIDVolumeTextureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	D3D11_SUBRESOURCE_DATA voxelDataArray;

	m_pChunkData = new Voxel[D3DVoxelConstants::VoxelsPerChunkAxisCube];
	int k = sizeof(Voxel);
	for(int i = 0; i < D3DVoxelConstants::VoxelsPerChunkAxisCube; i++)
	{
		m_pChunkData[i].cVolume = 0;
		m_pChunkData[i].cType = 1;
		m_pChunkData[i].cSubType = 1;
		m_pChunkData[i].cOwner = 1;
	}
	//testVoxelData[0] = 1;
	//testVoxelData[D3DVoxelConstants::CalcVoxelIndex(1,0,1)].cVolume = 255;
	if( 0 )
	{
	for(int xx = 0; xx < 2; xx++)
		for(int yy = 0; yy < 2; yy++)
			for(int zz = 0; zz < 2; zz++)
			{
				
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(1 + xx,1 + yy,1 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2 + xx,1 + yy,1 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(1 + xx,2 + yy,1 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(1 + xx,1 + yy,2 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(1 + xx,2 + yy,2 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2 + xx,1 + yy,2 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2 + xx,2 + yy,1 + zz)].cVolume = 255;
				m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2 + xx,2 + yy,2 + zz)].cVolume = 255;
			}
	}

	//m_pChunkData[D3DVoxelConstants::CalcVoxelIndex(2,1,1)].cVolume = 200;
	/*testVoxelData[8] = 1;
	testVoxelData[12] = 1;
	testVoxelData[16] = 1;
	testVoxelData[20] = 1;
	testVoxelData[24] = 1;
	testVoxelData[28] = 1;*/
	voxelDataArray.pSysMem = (void *) m_pChunkData;
	voxelDataArray.SysMemPitch = D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::BytesPerVoxel;
	voxelDataArray.SysMemSlicePitch = D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::VoxelsPerChunkAxis * D3DVoxelConstants::BytesPerVoxel;

	hr = m_pDevice->CreateTexture3D(&VertexIDVolumeTextureDesc, &voxelDataArray, &m_pVoxelData);
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateTexture3D:m_pVoxelData", L"Error", MB_OK );
        return hr;
    
	}
	D3D11_SHADER_RESOURCE_VIEW_DESC voxel3DTextureDesc;
	
	voxel3DTextureDesc.Texture3D.MostDetailedMip = 0;
	voxel3DTextureDesc.Texture3D.MipLevels = 1;
	voxel3DTextureDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
	voxel3DTextureDesc.Format = VertexIDVolumeTextureDesc.Format;
	hr = m_pDevice->CreateShaderResourceView(m_pVoxelData, &voxel3DTextureDesc, &m_pVoxelTextureSRV);
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateShaderResourceView:m_pVoxelTextureSRV", L"Error", MB_OK );
        return hr;
    
	}

	UpdateVoxelTexture();
	
	int m_nBufferSize = 10000000;

	D3D11_BUFFER_DESC bufferDesc =
	{
		m_nBufferSize,
		D3D11_USAGE_DEFAULT,
		D3D11_BIND_STREAM_OUTPUT | D3D11_BIND_VERTEX_BUFFER,
		0,
		0,
		0
	};
	hr = m_pDevice->CreateBuffer( &bufferDesc, NULL, &m_pVoxelMeshBuffer );
	if( FAILED( hr ) )
    {
        MessageBox( NULL,
                    L"CreateBuffer:m_pVoxelMeshBuffer", L"Error", MB_OK );
        return hr;
    
	}

	return hr;
}

void D3DVoxelChunk::Release(void)
{
	if( m_pChunkData ) delete[] m_pChunkData;
	if( m_pVoxelData ) m_pVoxelData->Release();
	if( m_pVoxelTextureSRV ) m_pVoxelTextureSRV->Release();
	if( m_pVoxelSamplerLinear ) m_pVoxelSamplerLinear->Release();
	if( m_pVoxelMeshBuffer ) m_pVoxelMeshBuffer->Release();
    if( m_pIndexBuffer ) m_pIndexBuffer->Release();
	if( m_pWorldMatrixBuffer ) m_pWorldMatrixBuffer->Release();
	
	
}
int D3DVoxelConstants::CalcVoxelIndex(int x, int y, int z)
{
	int ret = (x + NonOverlappedVoxelStart) + (y + NonOverlappedVoxelStart) * VoxelsPerChunkAxis + (z + NonOverlappedVoxelStart) * VoxelsPerChunkAxisSquared;
	return ret;
}
int D3DVoxelConstants::CalcVerticePlaceholderIndex(int x, int y, int z)
{
	int ret = (x) + (y) * NonOverlappedVoxelCount + (z) * NonOverlappedVoxelCount * NonOverlappedVoxelCount;
	return ret;
}

int D3DVoxelConstants::CalcChunkIndex(int x, int y, int z)
{
	int ret = x + y * CHUNK_AXIS_COUNT + z * CHUNK_AXIS_COUNT_SQUARED;
	return ret;
}

